#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem>
#include <sstream>
#include <fstream>
#include <memory>
#include <sys/types.h>
#include <unistd.h>
#include <ctime>
#include <time.h>
#include "mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;

    const std::string gsep = "\r\n";

    /* 1. 策略模式设计 C++多态 */
    /* 基类 */
    class LogStrategy
    {
    public:
        virtual ~LogStrategy() {}                              /* 虚函数防止内存泄漏 */
        virtual void SyncLogMessage(std::string &message) = 0; /* 设计成抽象函数要求子类必须重写虚函数 */
    };

    /* 子类 */
    /* [1] 显示器打印模式 */
    class ConsolePrintStrategy : public LogStrategy
    {
    public:
        ConsolePrintStrategy() {}
        virtual void SyncLogMessage(std::string &message) override
        {
            /* 向显示器打印 */
            LockGuard lockguard(_lock); /* 加锁 */
            std::cout << message << gsep;
        }
        ~ConsolePrintStrategy() {}

    private:
        Mutex _lock; /* 锁 */
    };

    /* [2] 文件写入模式 */
    // 文件打印日志的策略 : 子类
    const std::string defaultpath = "./log";
    const std::string defaultfile = "my.log";
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile)
            : _path(path),
            _file(file)
        {
            LockGuard lockguard(_mutex);
            if (std::filesystem::exists(_path))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        virtual void SyncLogMessage(std::string &message) override
        {
            LockGuard lockguard(_mutex);

            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"
            std::ofstream out(filename, std::ios::app);                              // 追加写入的 方式打开
            if (!out.is_open())
            {
                return;
            }
            out << message << gsep;
            out.close();
        }
        ~FileLogStrategy()
        {
        }

    private:
        std::string _path; // 日志文件所在路径
        std::string _file; // 日志文件本身
        Mutex _mutex;
    };

    /* Log类 */
    /* 1. 日志等级 */
    enum class LogLevel
    {
        DEBUG = 1, /* 测试 */
        INFO,      /* 正常消息 */
        WARING,    /* 告警 */
        ERROR,     /* 错误 */
        FATAL      /* 崩溃 */
    };

    std::string LevelToStr(LogLevel &level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARING:
            return "WARING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKOWN";
        }
    }

    /* 2. 获取当前时间 */
    std::string GetCurTime()
    {
        time_t curtime = ::time(nullptr);
        struct tm curstruct;
        localtime_r(&curtime, &curstruct);
        char timebuffer[1024];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curstruct.tm_year + 1900,
                 curstruct.tm_mon + 1,
                 curstruct.tm_mday,
                 curstruct.tm_hour,
                 curstruct.tm_min,
                 curstruct.tm_sec);
        return timebuffer;
    }

    class Logger
    {
    public:
        Logger()
        {
            /* 默认显示器策略 */
            EnableConsoleStrategy();
        }

        /* 文件策略 */
        void EnableFileStrategy()
        {
            _strategy = std::make_unique<FileLogStrategy>();
        }

        /* 显示器策略 */
        void EnableConsoleStrategy()
        {
            _strategy = std::make_unique<ConsolePrintStrategy>();
        }

        /* 内部类 */
        class LogMessage
        {
        public:
            LogMessage(LogLevel &level, std::string &filename, int line, Logger &logger)
                : _curtime(GetCurTime()),
                  _level(level),
                  _pid(getpid()),
                  _filename(filename),
                  _line(line),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _curtime << "] "
                   << "[" << LevelToStr(_level) << "] "
                   << "[" << _pid << "] "
                   << "[" << _filename << "] "
                   << "[" << _line << "] "
                   << " - ";
                _loginfo = ss.str();
            }

            // LogMessage() << "Hello World!";
            template<typename T>
            LogMessage& operator<<(const T& in)  /* 返回LogMessage& */
            {
                std::stringstream ss;
                ss << in;
                _loginfo += ss.str();
                return *this;
            }

            ~LogMessage()
            {
                if(_logger._strategy)
                {
                    /* 刷新日志信息 */
                    _logger._strategy->SyncLogMessage(_loginfo);
                }
            }

        private:
            std::string _curtime;  /* 当前时间 */
            LogLevel _level;       /* 日志等级 */
            pid_t _pid;            /* 当前执行流pid */
            std::string _filename; /* 日志文件名 */
            int _line;             /* 行号 */
            std::string _loginfo;  /* 总体日志信息 */
            Logger &_logger;       /* 日志对象 */
        };

        LogMessage operator()(LogLevel level, std::string name, int line)
        {
            return LogMessage(level, name, line, *this);
        }

        ~Logger()
        {
        }

    private:
        /* 指针指针管理策略模式对象 */
        std::unique_ptr<LogStrategy> _strategy;
    };

   // 全局日志对象
   Logger log;
    
   // 使用宏，简化用户操作，获取文件名和行号
   #define LOG(level) log(level, __FILE__, __LINE__)
   #define Enable_Console_Log_Strategy() log.EnableConsoleStrategy();
   #define Enable_File_Log_Strategy() log.EnableFileStrategy();
}